1560 lines
52 KiB
Python
1560 lines
52 KiB
Python
import json
|
|
import urllib.parse
|
|
from datetime import date, timedelta
|
|
|
|
import mock
|
|
import pytest
|
|
from django.apps import apps
|
|
from django.contrib.auth.models import Group, User
|
|
from django.db import connection
|
|
from django.db.migrations.executor import MigrationExecutor
|
|
from django.test import override_settings
|
|
from django.utils import timezone
|
|
from httmock import HTTMock, remember_called, urlmatch, with_httmock
|
|
from requests.exceptions import HTTPError
|
|
|
|
from combo.apps.dataviz.models import ChartNgCell, Gauge, Statistic, UnsupportedDataSet
|
|
from combo.data.models import Page, ValidityInfo
|
|
|
|
from .test_public import login, normal_user
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
@pytest.fixture
|
|
def cell():
|
|
page = Page(title='One', slug='index')
|
|
page.save()
|
|
|
|
cell = Gauge(page=page, order=0, placeholder='content')
|
|
cell.data_source = '[test_url]/XXX'
|
|
cell.save()
|
|
|
|
return cell
|
|
|
|
|
|
def test_jsonp_gauge(app, cell):
|
|
with override_settings(TEMPLATE_VARS={'test_url': 'http://www.example.net'}):
|
|
resp = app.get('/')
|
|
assert 'data-gauge-count-jsonp-url="http://www.example.net/XXX"' in resp.text
|
|
|
|
|
|
def test_json_gauge(app, cell):
|
|
cell.jsonp_data_source = False
|
|
cell.save()
|
|
with override_settings(TEMPLATE_VARS={'test_url': 'http://www.example.net'}):
|
|
with mock.patch('combo.apps.dataviz.views.requests.get') as requests_get:
|
|
requests_get.return_value = mock.Mock(content='xxx', status_code=200)
|
|
resp = app.get('/')
|
|
assert 'data-gauge-count-url="/ajax/gauge-count/%s/"' % cell.id in resp.text
|
|
resp = app.get('/ajax/gauge-count/%s/' % cell.id)
|
|
assert resp.text == 'xxx'
|
|
assert requests_get.call_args[0][0] == 'http://www.example.net/XXX'
|
|
|
|
|
|
VISUALIZATION_JSON = [
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/1/json/',
|
|
'path': 'https://bijoe.example.com/visualization/1/iframe/?signature=123',
|
|
'name': 'example visualization (X)',
|
|
'slug': 'example',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/2/json/',
|
|
'path': 'https://bijoe.example.com/visualization/2/iframe/?signature=123',
|
|
'name': 'second visualization (Y)',
|
|
'slug': 'second',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/3/json/',
|
|
'path': 'https://bijoe.example.com/visualization/3/iframe/?signature=123',
|
|
'name': 'third visualization (X/Y)',
|
|
'slug': 'third',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/4/json/',
|
|
'path': 'https://bijoe.example.com/visualization/4/iframe/?signature=123',
|
|
'name': 'fourth visualization (no axis)',
|
|
'slug': 'fourth',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/5/json/',
|
|
'path': 'https://bijoe.example.com/visualization/5/iframe/?signature=123',
|
|
'name': 'fifth visualization (loop/X)',
|
|
'slug': 'fifth',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/6/json/',
|
|
'path': 'https://bijoe.example.com/visualization/6/iframe/?signature=123',
|
|
'name': 'sixth visualization (loop/Y)',
|
|
'slug': 'sixth',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/7/json/',
|
|
'path': 'https://bijoe.example.com/visualization/7/iframe/?signature=123',
|
|
'name': 'seventh visualization (loop/X/Y)',
|
|
'slug': 'seventh',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/8/json/',
|
|
'path': 'https://bijoe.example.com/visualization/8/iframe/?signature=123',
|
|
'name': 'eighth visualization (duration)',
|
|
'slug': 'eighth',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/9/json/',
|
|
'path': 'https://bijoe.example.com/visualization/9/iframe/?signature=123',
|
|
'name': 'nineth visualization (loop over varying dimensions)',
|
|
'slug': 'nineth',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/10/json/',
|
|
'path': 'https://bijoe.example.com/visualization/10/iframe/?signature=123',
|
|
'name': 'tenth visualization (percents)',
|
|
'slug': 'tenth',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/11/json/',
|
|
'path': 'https://bijoe.example.com/visualization/11/iframe/?signature=123',
|
|
'name': 'eleventh visualization (not found)',
|
|
'slug': 'eleventh',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/12/json/',
|
|
'path': 'https://bijoe.example.com/visualization/12/iframe/?signature=123',
|
|
'name': 'twelth visualization (all null)',
|
|
'slug': 'twelth',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/13/json/',
|
|
'path': 'https://bijoe.example.com/visualization/13/iframe/?signature=123',
|
|
'name': 'thirteenth visualization (loop with empty x_labels)',
|
|
'slug': 'thirteenth',
|
|
},
|
|
{
|
|
'data-url': 'https://bijoe.example.com/visualization/14/json/',
|
|
'path': 'https://bijoe.example.com/visualization/14/iframe/?signature=123',
|
|
'name': 'fourteenth visualization (empty data)',
|
|
'slug': 'fourteenth',
|
|
},
|
|
]
|
|
|
|
|
|
def bijoe_mock(url, request):
|
|
if url.path == '/visualization/json/':
|
|
return {'content': json.dumps(VISUALIZATION_JSON), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/1/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [222, 134, 0, 53],
|
|
'axis': {'x_labels': ['web', 'mail', 'phone', 'email']},
|
|
'measure': 'integer',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/2/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [222, 134, 0, 53],
|
|
'axis': {'y_labels': ['web', 'mail', 'phone', 'email']},
|
|
'measure': 'integer',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/3/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [
|
|
[222, 134, 0, 53],
|
|
[122, 114, 2, 33],
|
|
],
|
|
'axis': {
|
|
'x_labels': ['web', 'mail', 'phone', 'email'],
|
|
'y_labels': ['foo', 'bar'],
|
|
},
|
|
'measure': 'integer',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/4/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': 222,
|
|
'axis': {},
|
|
'measure': 'integer',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/5/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [
|
|
[222, 134, 0, 53],
|
|
[122, 114, 2, 33],
|
|
],
|
|
'axis': {
|
|
'x_labels': ['web', 'mail', 'phone', 'email'],
|
|
'loop': ['foo', 'bar'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/6/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [
|
|
[222, 134, 0, 53],
|
|
[122, 114, 2, 33],
|
|
],
|
|
'axis': {
|
|
'y_labels': ['web', 'mail', 'phone', 'email'],
|
|
'loop': ['foo', 'bar'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/7/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [
|
|
[[222, 134, 0, 53], [122, 114, 2, 33]],
|
|
[[222, 134, 0, 53], [122, 114, 2, 33]],
|
|
[[222, 134, 0, 53], [122, 114, 2, 33]],
|
|
[[222, 134, 0, 53], [122, 114, 2, 33]],
|
|
],
|
|
'axis': {
|
|
'x_labels': ['foo', 'bar'],
|
|
'y_labels': ['web', 'mail', 'phone', 'email'],
|
|
'loop': ['a', 'b', 'c', 'd'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/8/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [1000, 123000, 8600, 86400],
|
|
'axis': {'y_labels': ['web', 'mail', 'email', 'fax']},
|
|
'unit': 'seconds',
|
|
'measure': 'duration',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/9/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [
|
|
[1, 1, 1, 1],
|
|
[1],
|
|
[1, 1],
|
|
],
|
|
'axis': {
|
|
'y_labels': ['web', 'mail', 'email'],
|
|
'loop': ['a', 'b', 'c', 'd'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/10/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [10, 20, 30, 40, 0],
|
|
'axis': {'y_labels': ['web', 'mail', 'email', 'fax', 'phone']},
|
|
'unit': None,
|
|
'measure': 'percent',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/11/json/':
|
|
response = {
|
|
'detail': 'not found',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 404}
|
|
if url.path == '/visualization/12/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [None, None],
|
|
'axis': {'x_labels': ['web', 'mail']},
|
|
'measure': 'integer',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/13/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [[[], []], [[], []], [[], []]],
|
|
'axis': {
|
|
'x_labels': [],
|
|
'y_labels': ['web', 'mail'],
|
|
'loop': ['a', 'b', 'c'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/visualization/14/json/':
|
|
response = {
|
|
'format': '1',
|
|
'data': [],
|
|
'axis': {
|
|
'x_labels': ['a', 'b', 'c'],
|
|
'y_labels': [],
|
|
},
|
|
'measure': 'integer',
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
|
|
|
|
STATISTICS_LIST = {
|
|
'data': [
|
|
{
|
|
'url': 'https://authentic.example.com/api/statistics/one-serie/',
|
|
'name': 'One serie stat',
|
|
'id': 'one-serie',
|
|
"filters": [
|
|
{
|
|
"default": "month",
|
|
"id": "time_interval",
|
|
"label": "Time interval",
|
|
"options": [
|
|
{"id": "day", "label": "Day"},
|
|
{"id": "month", "label": "Month"},
|
|
{"id": "year", "label": "Year"},
|
|
],
|
|
"required": True,
|
|
},
|
|
{
|
|
"id": "ou",
|
|
"label": "Organizational Unit",
|
|
"options": [
|
|
{"id": "default", "label": "Default OU"},
|
|
{"id": "other", "label": "Other OU"},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
'url': 'https://authentic.example.com/api/statistics/two-series/',
|
|
'name': 'Two series stat',
|
|
'id': 'two-series',
|
|
},
|
|
{
|
|
'url': 'https://authentic.example.com/api/statistics/no-data/',
|
|
'name': 'No data stat',
|
|
'id': 'no-data',
|
|
},
|
|
{
|
|
'url': 'https://authentic.example.com/api/statistics/not-found/',
|
|
'name': '404 not found stat',
|
|
'id': 'not-found',
|
|
},
|
|
{
|
|
'url': 'https://authentic.example.com/api/statistics/daily/',
|
|
'name': 'Daily discontinuous serie',
|
|
'id': 'daily',
|
|
"filters": [
|
|
{
|
|
"default": "day",
|
|
"id": "time_interval",
|
|
"label": "Time interval",
|
|
"options": [
|
|
{"id": "day", "label": "Day"},
|
|
],
|
|
"required": True,
|
|
}
|
|
],
|
|
},
|
|
{
|
|
'url': 'https://authentic.example.com/api/statistics/leap-week/',
|
|
'name': 'Same week spanning two years',
|
|
'id': 'leap-week',
|
|
"filters": [
|
|
{
|
|
"default": "day",
|
|
"id": "time_interval",
|
|
"label": "Time interval",
|
|
"options": [
|
|
{"id": "day", "label": "Day"},
|
|
],
|
|
"required": True,
|
|
}
|
|
],
|
|
},
|
|
]
|
|
}
|
|
|
|
|
|
@remember_called
|
|
def new_api_mock(url, request):
|
|
if url.path == '/api/statistics/':
|
|
return {'content': json.dumps(STATISTICS_LIST), 'request': request, 'status_code': 200}
|
|
if url.path == '/api/statistics/one-serie/':
|
|
response = {
|
|
'data': {
|
|
'series': [{'data': [None, 16, 2], 'label': 'Serie 1'}],
|
|
'x_labels': ['2020-10', '2020-11', '2020-12'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/api/statistics/two-series/':
|
|
response = {
|
|
'data': {
|
|
'series': [
|
|
{'data': [None, 16, 2], 'label': 'Serie 1'},
|
|
{'data': [2, 1, None], 'label': 'Serie 2'},
|
|
],
|
|
'x_labels': ['2020-10', '2020-11', '2020-12'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/api/statistics/no-data/':
|
|
response = {
|
|
'data': {'series': [], 'x_labels': []},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/api/statistics/not-found/':
|
|
return {'content': b'', 'request': request, 'status_code': 404}
|
|
if url.path == '/api/statistics/daily/':
|
|
response = {
|
|
'data': {
|
|
'series': [
|
|
{'data': [None, 1, 16, 2], 'label': 'Serie 1'},
|
|
{'data': [2, 2, 1, None], 'label': 'Serie 2'},
|
|
],
|
|
'x_labels': ['2020-10-06', '2020-10-13', '2020-11-30', '2022-02-01'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
if url.path == '/api/statistics/leap-week/':
|
|
response = {
|
|
'data': {
|
|
'series': [
|
|
{'data': [None, 1, 16, 2], 'label': 'Serie 1'},
|
|
],
|
|
'x_labels': ['2020-12-30', '2020-12-31', '2021-01-01', '2021-01-02'],
|
|
},
|
|
}
|
|
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
|
|
|
|
|
|
@pytest.fixture
|
|
@with_httmock(bijoe_mock)
|
|
def statistics(settings):
|
|
settings.KNOWN_SERVICES = {
|
|
"bijoe": {
|
|
"plop": {"title": "test", "url": "https://bijoe.example.com", "secret": "combo", "orig": "combo"}
|
|
}
|
|
}
|
|
settings.STATISTICS_PROVIDERS = ['bijoe']
|
|
appconfig = apps.get_app_config('dataviz')
|
|
appconfig.hourly()
|
|
assert Statistic.objects.count() == len(VISUALIZATION_JSON)
|
|
|
|
|
|
@pytest.fixture
|
|
@with_httmock(new_api_mock)
|
|
def new_api_statistics(settings):
|
|
settings.KNOWN_SERVICES = {
|
|
'authentic': {
|
|
'connection': {
|
|
'title': 'Connection',
|
|
'url': 'https://authentic.example.com',
|
|
'secret': 'combo',
|
|
'orig': 'combo',
|
|
}
|
|
}
|
|
}
|
|
settings.STATISTICS_PROVIDERS = ['authentic']
|
|
appconfig = apps.get_app_config('dataviz')
|
|
appconfig.hourly()
|
|
assert Statistic.objects.count() == len(STATISTICS_LIST['data'])
|
|
|
|
|
|
@with_httmock(bijoe_mock)
|
|
def test_chartng_cell(app, statistics):
|
|
page = Page(title='One', slug='index')
|
|
page.save()
|
|
|
|
cell = ChartNgCell(page=page, order=1)
|
|
cell.statistic = Statistic.objects.get(slug='example')
|
|
cell.save()
|
|
|
|
# bar
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Bar'
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [([222, 134, 0, 53], {'title': ''})]
|
|
|
|
# horizontal bar
|
|
cell.chart_type = 'horizontal-bar'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'HorizontalBar'
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [([222, 134, 0, 53], {'title': ''})]
|
|
|
|
# pie
|
|
cell.chart_type = 'pie'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Pie'
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222], {'title': u'web'}),
|
|
([134], {'title': u'mail'}),
|
|
([53], {'title': u'email'}),
|
|
]
|
|
|
|
# data in Y
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='second')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [([222, 134, 0, 53], {'title': ''})]
|
|
|
|
# data in X/Y
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='third')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# stacked bar with percent
|
|
cell.chart_type = 'stacked-bar-percent'
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([64.5, 54, 0, 61.6], {'title': u'foo'}),
|
|
([35.5, 46, 100, 38.4], {'title': u'bar'}),
|
|
]
|
|
assert all(x + y == 100 for x, y in zip(chart.raw_series[0][0], chart.raw_series[1][0]))
|
|
|
|
# single data point
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='fourth')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['']
|
|
assert chart.raw_series == [([222], {'title': ''})]
|
|
|
|
# loop/X
|
|
cell.statistic = Statistic.objects.get(slug='fifth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# loop/Y
|
|
cell.statistic = Statistic.objects.get(slug='sixth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# loop/X/Y
|
|
cell.statistic = Statistic.objects.get(slug='seventh')
|
|
cell.save()
|
|
with pytest.raises(UnsupportedDataSet):
|
|
chart = cell.get_chart()
|
|
|
|
# duration
|
|
cell.statistic = Statistic.objects.get(slug='eighth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
|
|
# loop/X/Y
|
|
cell.statistic = Statistic.objects.get(slug='nineth')
|
|
cell.save()
|
|
with pytest.raises(UnsupportedDataSet):
|
|
chart = cell.get_chart()
|
|
|
|
# deleted visualization
|
|
cell.statistic = Statistic.objects.get(slug='eleventh')
|
|
cell.save()
|
|
with pytest.raises(HTTPError):
|
|
chart = cell.get_chart()
|
|
|
|
# loop and empty x_labels
|
|
cell.statistic = Statistic.objects.get(slug='thirteenth')
|
|
cell.save()
|
|
with pytest.raises(UnsupportedDataSet):
|
|
chart = cell.get_chart()
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_chartng_cell_new_api(app, new_api_statistics):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell(page=page, order=1)
|
|
cell.statistic = Statistic.objects.get(slug='one-serie')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Bar'
|
|
assert chart.x_labels == ['2020-10', '2020-11', '2020-12']
|
|
assert chart.raw_series == [
|
|
(
|
|
[None, 16, 2],
|
|
{'title': 'Serie 1'},
|
|
)
|
|
]
|
|
|
|
cell.chart_type = 'pie'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Pie'
|
|
assert chart.x_labels == ['2020-10', '2020-11', '2020-12']
|
|
assert chart.raw_series == [
|
|
([16], {'title': '2020-11'}),
|
|
([2], {'title': '2020-12'}),
|
|
]
|
|
|
|
cell.statistic = Statistic.objects.get(slug='two-series')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['2020-10', '2020-11', '2020-12']
|
|
assert chart.raw_series == [([None, 16, 2], {'title': 'Serie 1'}), ([2, 1, None], {'title': 'Serie 2'})]
|
|
|
|
# stacked bar with percent
|
|
cell.chart_type = 'stacked-bar-percent'
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['2020-10', '2020-11', '2020-12']
|
|
assert chart.raw_series == [
|
|
([None, 94.1, 100], {'title': 'Serie 1'}),
|
|
([100, 5.9, None], {'title': 'Serie 2'}),
|
|
]
|
|
assert all(x + y == 100 for x, y in zip(chart.raw_series[0][0], chart.raw_series[1][0]) if x and y)
|
|
|
|
cell.statistic = Statistic.objects.get(slug='no-data')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == []
|
|
assert chart.raw_series == []
|
|
|
|
cell.statistic = Statistic.objects.get(slug='not-found')
|
|
cell.save()
|
|
with pytest.raises(HTTPError):
|
|
chart = cell.get_chart()
|
|
|
|
|
|
@with_httmock(bijoe_mock)
|
|
def test_chartng_cell_hide_null_values(app, statistics):
|
|
page = Page(title='One', slug='index')
|
|
page.save()
|
|
|
|
cell = ChartNgCell(page=page, order=1)
|
|
cell.statistic = Statistic.objects.get(slug='example')
|
|
cell.hide_null_values = True
|
|
cell.save()
|
|
|
|
# bar
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Bar'
|
|
assert chart.x_labels == ['web', 'mail', 'email']
|
|
assert chart.raw_series == [([222, 134, 53], {'title': ''})]
|
|
|
|
# horizontal bar
|
|
cell.chart_type = 'horizontal-bar'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'HorizontalBar'
|
|
assert chart.x_labels == ['web', 'mail', 'email']
|
|
assert chart.raw_series == [([222, 134, 53], {'title': ''})]
|
|
|
|
# pie
|
|
cell.chart_type = 'pie'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Pie'
|
|
assert chart.x_labels == ['web', 'mail', 'email']
|
|
assert chart.raw_series == [
|
|
([222], {'title': u'web'}),
|
|
([134], {'title': u'mail'}),
|
|
([53], {'title': u'email'}),
|
|
]
|
|
|
|
# data in Y
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='second')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'email']
|
|
assert chart.raw_series == [([222, 134, 53], {'title': ''})]
|
|
|
|
# data in X/Y
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='third')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# single data point
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='fourth')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['']
|
|
assert chart.raw_series == [([222], {'title': ''})]
|
|
|
|
# loop/X
|
|
cell.statistic = Statistic.objects.get(slug='fifth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# loop/Y
|
|
cell.statistic = Statistic.objects.get(slug='sixth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# all null
|
|
cell.statistic = Statistic.objects.get(slug='twelth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == []
|
|
assert chart.raw_series == [([], {'title': ''})]
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_chartng_cell_hide_null_values_new_api(app, new_api_statistics):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell(page=page, order=1, hide_null_values=True)
|
|
cell.statistic = Statistic.objects.get(slug='one-serie')
|
|
cell.hide_null_values = True
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['2020-11', '2020-12']
|
|
assert chart.raw_series == [
|
|
(
|
|
[16, 2],
|
|
{'title': 'Serie 1'},
|
|
)
|
|
]
|
|
|
|
|
|
@with_httmock(bijoe_mock)
|
|
def test_chartng_cell_sort_order_alpha(app, statistics):
|
|
page = Page(title='One', slug='index')
|
|
page.save()
|
|
|
|
cell = ChartNgCell(page=page, order=1)
|
|
cell.statistic = Statistic.objects.get(slug='example')
|
|
cell.sort_order = 'alpha'
|
|
cell.save()
|
|
|
|
# bar
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Bar'
|
|
assert chart.x_labels == ['email', 'mail', 'phone', 'web']
|
|
assert chart.raw_series == [([53, 134, 0, 222], {'title': ''})]
|
|
|
|
# horizontal bar
|
|
cell.chart_type = 'horizontal-bar'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'HorizontalBar'
|
|
assert chart.x_labels == ['email', 'mail', 'phone', 'web']
|
|
assert chart.raw_series == [([53, 134, 0, 222], {'title': ''})]
|
|
|
|
# pie
|
|
cell.chart_type = 'pie'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Pie'
|
|
assert chart.x_labels == ['email', 'mail', 'phone', 'web']
|
|
assert chart.raw_series == [
|
|
([53], {'title': u'email'}),
|
|
([134], {'title': u'mail'}),
|
|
([222], {'title': u'web'}),
|
|
]
|
|
|
|
# data in Y
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='second')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['email', 'mail', 'phone', 'web']
|
|
assert chart.raw_series == [([53, 134, 0, 222], {'title': ''})]
|
|
|
|
# data in X/Y
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='third')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# single data point
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='fourth')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['']
|
|
assert chart.raw_series == [([222], {'title': ''})]
|
|
|
|
# loop/X
|
|
cell.statistic = Statistic.objects.get(slug='fifth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# loop/Y
|
|
cell.statistic = Statistic.objects.get(slug='sixth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# empty data
|
|
cell.statistic = Statistic.objects.get(slug='fourteenth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['a', 'b', 'c']
|
|
assert chart.raw_series == [([], {'title': ''})]
|
|
|
|
|
|
@with_httmock(bijoe_mock)
|
|
def test_chartng_cell_sort_order_desc(app, statistics):
|
|
page = Page(title='One', slug='index')
|
|
page.save()
|
|
|
|
cell = ChartNgCell(page=page, order=1)
|
|
cell.statistic = Statistic.objects.get(slug='example')
|
|
cell.sort_order = 'desc'
|
|
cell.save()
|
|
|
|
# bar
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Bar'
|
|
assert chart.x_labels == ['web', 'mail', 'email', 'phone']
|
|
assert chart.raw_series == [([222, 134, 53, 0], {'title': ''})]
|
|
|
|
# horizontal bar
|
|
cell.chart_type = 'horizontal-bar'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'HorizontalBar'
|
|
assert chart.x_labels == ['web', 'mail', 'email', 'phone']
|
|
assert chart.raw_series == [([222, 134, 53, 0], {'title': ''})]
|
|
|
|
# pie
|
|
cell.chart_type = 'pie'
|
|
chart = cell.get_chart()
|
|
assert chart.__class__.__name__ == 'Pie'
|
|
assert chart.x_labels == ['web', 'mail', 'email', 'phone']
|
|
assert chart.raw_series == [
|
|
([222], {'title': u'web'}),
|
|
([134], {'title': u'mail'}),
|
|
([53], {'title': u'email'}),
|
|
]
|
|
|
|
# data in Y
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='second')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'email', 'phone']
|
|
assert chart.raw_series == [([222, 134, 53, 0], {'title': ''})]
|
|
|
|
# data in X/Y
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='third')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# single data point
|
|
cell.chart_type = 'bar'
|
|
cell.statistic = Statistic.objects.get(slug='fourth')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['']
|
|
assert chart.raw_series == [([222], {'title': ''})]
|
|
|
|
# loop/X
|
|
cell.statistic = Statistic.objects.get(slug='fifth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# loop/Y
|
|
cell.statistic = Statistic.objects.get(slug='sixth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
|
|
assert chart.raw_series == [
|
|
([222, 134, 0, 53], {'title': u'foo'}),
|
|
([122, 114, 2, 33], {'title': u'bar'}),
|
|
]
|
|
|
|
# empty data
|
|
cell.statistic = Statistic.objects.get(slug='fourteenth')
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['a', 'b', 'c']
|
|
assert chart.raw_series == [([], {'title': ''})]
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_chartng_cell_sort_order_new_api(app, new_api_statistics):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell(page=page, order=1)
|
|
cell.statistic = Statistic.objects.get(slug='one-serie')
|
|
cell.sort_order = 'desc'
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
assert chart.x_labels == ['2020-11', '2020-12', '2020-10']
|
|
assert chart.raw_series == [
|
|
(
|
|
[16, 2, None],
|
|
{'title': 'Serie 1'},
|
|
)
|
|
]
|
|
|
|
|
|
@with_httmock(bijoe_mock)
|
|
def test_chartng_cell_view(app, normal_user, statistics):
|
|
page = Page(title='One', slug='index')
|
|
page.save()
|
|
|
|
cell = ChartNgCell(page=page, order=1, placeholder='content')
|
|
cell.statistic = Statistic.objects.get(slug='example')
|
|
cell.save()
|
|
location = '/api/dataviz/graph/%s/' % cell.id
|
|
resp = app.get(location) # get data in cache
|
|
resp = app.get('/')
|
|
assert 'min-height: 250px' in resp.text
|
|
assert location in resp.text
|
|
|
|
resp = app.get(location + '?width=400')
|
|
assert resp.content_type == 'image/svg+xml'
|
|
|
|
resp = app.get(location + '?width=') # no crash
|
|
assert resp.content_type == 'image/svg+xml'
|
|
|
|
page.public = False
|
|
page.save()
|
|
resp = app.get(location + '?width=400', status=403)
|
|
|
|
page.public = True
|
|
page.save()
|
|
group = Group(name='plop')
|
|
group.save()
|
|
cell.public = False
|
|
cell.groups.set([group])
|
|
cell.save()
|
|
resp = app.get(location + '?width=400', status=403)
|
|
|
|
app = login(app, username='normal-user', password='normal-user')
|
|
resp = app.get(location + '?width=400', status=403)
|
|
|
|
normal_user.groups.set([group])
|
|
normal_user.save()
|
|
resp = app.get(location + '?width=400', status=200)
|
|
|
|
# table visualization
|
|
cell.chart_type = 'table'
|
|
cell.save()
|
|
resp = app.get('/')
|
|
assert '<td>222</td>' in resp.text
|
|
|
|
# unsupported dataset
|
|
cell.statistic = Statistic.objects.get(slug='seventh')
|
|
cell.save()
|
|
resp = app.get(location) # get data in cache
|
|
resp = app.get('/')
|
|
assert 'Unsupported dataset' in resp.text
|
|
|
|
cell.chart_type = 'bar'
|
|
cell.save()
|
|
resp = app.get(location + '?width=400', status=200)
|
|
assert 'Unsupported dataset' in resp.text
|
|
|
|
# durations
|
|
cell.statistic = Statistic.objects.get(slug='eighth')
|
|
cell.chart_type = 'table'
|
|
cell.save()
|
|
resp = app.get(location) # get data in cache
|
|
resp = app.get('/')
|
|
assert '<td>Less than an hour</td>' in resp.text
|
|
assert '<td>1 day and 10 hours</td>' in resp.text
|
|
assert '<td>2 hours</td>' in resp.text
|
|
assert '<td>1 day</td>' in resp.text
|
|
|
|
cell.chart_type = 'bar'
|
|
cell.save()
|
|
resp = app.get(location + '?width=400', status=200)
|
|
assert '>Less than an hour<' in resp.text
|
|
assert '>1 day and 10 hours<' in resp.text
|
|
assert '>2 hours<' in resp.text
|
|
assert '>1 day<' in resp.text
|
|
|
|
# percents
|
|
cell.statistic = Statistic.objects.get(slug='tenth')
|
|
cell.chart_type = 'table'
|
|
cell.save()
|
|
resp = app.get(location) # get data in cache
|
|
resp = app.get('/')
|
|
assert '<td>10.0%</td>' in resp.text
|
|
|
|
cell.chart_type = 'bar'
|
|
cell.save()
|
|
resp = app.get(location + '?width=400', status=200)
|
|
assert '>10.0%<' in resp.text
|
|
|
|
# deleted visualization
|
|
cell.statistic = Statistic.objects.get(slug='eleventh')
|
|
cell.save()
|
|
resp = app.get(location)
|
|
assert 'not found' in resp.text
|
|
|
|
# cell with no statistic chosen
|
|
cell.chart_type = 'table'
|
|
cell.statistic = None
|
|
cell.save()
|
|
resp = app.get('/')
|
|
assert not 'cell' in resp.text
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_chartng_cell_view_new_api(app, normal_user, new_api_statistics):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell(page=page, order=1, placeholder='content')
|
|
cell.statistic = Statistic.objects.get(slug='one-serie')
|
|
cell.save()
|
|
|
|
location = '/api/dataviz/graph/%s/' % cell.id
|
|
resp = app.get('/')
|
|
assert 'min-height: 250px' in resp.text
|
|
assert location in resp.text
|
|
|
|
# table visualization
|
|
cell.chart_type = 'table'
|
|
cell.save()
|
|
resp = app.get(location) # populate cache
|
|
resp = app.get('/')
|
|
assert '<td>18</td>' in resp.text
|
|
|
|
# deleted visualization
|
|
cell.statistic = Statistic.objects.get(slug='not-found')
|
|
cell.save()
|
|
resp = app.get(location)
|
|
assert 'not found' in resp.text
|
|
|
|
cell.statistic.url = ''
|
|
cell.statistic.save()
|
|
resp = app.get(location, status=404)
|
|
|
|
|
|
@with_httmock(bijoe_mock)
|
|
def test_chartng_cell_manager(app, admin_user, statistics):
|
|
page = Page(title='One', slug='index')
|
|
page.save()
|
|
Statistic.objects.create(
|
|
slug='unavailable-stat', label='Unavailable Stat', site_slug='plop', available=False
|
|
)
|
|
|
|
app = login(app)
|
|
|
|
cell = ChartNgCell.objects.create(page=page, order=1, placeholder='content')
|
|
resp = app.get('/manage/pages/%s/' % page.id)
|
|
assert 'time_range' not in resp.form.fields
|
|
assert 'time_range_start' not in resp.form.fields
|
|
assert 'time_range_end' not in resp.form.fields
|
|
|
|
cell.statistic = Statistic.objects.get(slug='example')
|
|
cell.save()
|
|
resp = app.get('/manage/pages/%s/' % page.id)
|
|
statistics_field = resp.form['cdataviz_chartngcell-%s-statistic' % cell.id]
|
|
# available visualizations and a blank choice
|
|
assert len(statistics_field.options) == len(VISUALIZATION_JSON) + 1
|
|
assert statistics_field.value == str(cell.statistic.pk)
|
|
assert statistics_field.options[1][2] == 'test: eighth visualization (duration)'
|
|
assert not 'Unavailable Stat' in resp.text
|
|
assert 'time_range' not in resp.form.fields
|
|
assert 'time_range_start' not in resp.form.fields
|
|
assert 'time_range_end' not in resp.form.fields
|
|
|
|
cell.statistic = Statistic.objects.get(slug='unavailable-stat')
|
|
cell.save()
|
|
resp = app.get('/manage/pages/%s/' % page.id)
|
|
statistics_field = resp.form['cdataviz_chartngcell-%s-statistic' % cell.id]
|
|
# available visualizations, a blank choice and the current unavailable visualization
|
|
assert len(statistics_field.options) == len(VISUALIZATION_JSON) + 2
|
|
assert 'Unavailable Stat' in resp.text
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_chartng_cell_manager_new_api(app, admin_user, new_api_statistics):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell(page=page, order=1, placeholder='content')
|
|
cell.statistic = Statistic.objects.get(slug='one-serie')
|
|
cell.save()
|
|
|
|
app = login(app)
|
|
resp = app.get('/manage/pages/%s/' % page.id)
|
|
field_prefix = 'cdataviz_chartngcell-%s-' % cell.id
|
|
statistics_field = resp.form[field_prefix + 'statistic']
|
|
assert len(statistics_field.options) == len(STATISTICS_LIST['data']) + 1
|
|
assert statistics_field.value == str(cell.statistic.pk)
|
|
assert statistics_field.options[4][2] == 'Connection: One serie stat'
|
|
|
|
time_interval_field = resp.form[field_prefix + 'time_interval']
|
|
assert time_interval_field.pos == statistics_field.pos + 1
|
|
assert time_interval_field.value == 'month'
|
|
assert time_interval_field.options == [
|
|
('day', False, 'Day'),
|
|
('month', True, 'Month'),
|
|
('year', False, 'Year'),
|
|
('_week', False, 'Week'),
|
|
('_weekday', False, 'Week day'),
|
|
]
|
|
|
|
ou_field = resp.form[field_prefix + 'ou']
|
|
assert ou_field.pos == statistics_field.pos + 2
|
|
assert ou_field.value == ''
|
|
assert ou_field.options == [
|
|
('', True, '---------'),
|
|
('default', False, 'Default OU'),
|
|
('other', False, 'Other OU'),
|
|
]
|
|
resp.form[field_prefix + 'ou'] = 'default'
|
|
|
|
resp = resp.form.submit().follow()
|
|
assert resp.form[field_prefix + 'ou'].value == 'default'
|
|
cell.refresh_from_db()
|
|
assert cell.filter_params == {'ou': 'default', 'time_interval': 'month'}
|
|
resp.form[field_prefix + 'ou'] = ''
|
|
|
|
resp = resp.form.submit().follow()
|
|
assert resp.form[field_prefix + 'ou'].value == ''
|
|
cell.refresh_from_db()
|
|
assert cell.filter_params == {'time_interval': 'month'}
|
|
|
|
resp.form[field_prefix + 'time_range'] = 'previous-year'
|
|
resp = resp.form.submit().follow()
|
|
cell.refresh_from_db()
|
|
assert cell.time_range == 'previous-year'
|
|
|
|
resp.form[field_prefix + 'time_range'] = 'range'
|
|
resp.form[field_prefix + 'time_range_start'] = '2020-10-01'
|
|
resp.form[field_prefix + 'time_range_end'] = '2020-11-03'
|
|
resp = resp.form.submit().follow()
|
|
cell.refresh_from_db()
|
|
assert cell.time_range == 'range'
|
|
assert cell.time_range_start == date(year=2020, month=10, day=1)
|
|
assert cell.time_range_end == date(year=2020, month=11, day=3)
|
|
|
|
resp.form[field_prefix + 'time_range_start'] = ''
|
|
resp.form[field_prefix + 'time_range_end'] = ''
|
|
resp = resp.form.submit().follow()
|
|
cell.refresh_from_db()
|
|
assert cell.time_range_start is None
|
|
assert cell.time_range_end is None
|
|
|
|
no_filters_stat = Statistic.objects.get(slug='two-series')
|
|
resp.form[field_prefix + 'statistic'] = no_filters_stat.pk
|
|
resp = resp.form.submit().follow()
|
|
assert resp.form[field_prefix + 'statistic'].value == str(no_filters_stat.pk)
|
|
assert field_prefix + 'time_interval' not in resp.form.fields
|
|
assert field_prefix + 'ou' not in resp.form.fields
|
|
cell.refresh_from_db()
|
|
assert cell.filter_params == {}
|
|
assert cell.time_range == ''
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_chartng_cell_manager_new_api_dynamic_fields(app, admin_user, new_api_statistics):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell.objects.create(page=page, order=1, placeholder='content')
|
|
statistic = Statistic.objects.get(slug='one-serie')
|
|
|
|
app = login(app)
|
|
resp = app.get('/manage/pages/%s/' % page.id)
|
|
field_prefix = 'cdataviz_chartngcell-%s-' % cell.id
|
|
resp.form[field_prefix + 'statistic'] = statistic.pk
|
|
resp = app.post(resp.form.action, params=resp.form.submit_fields(), xhr=True)
|
|
assert 'time_interval' in resp.text
|
|
|
|
|
|
@with_httmock(bijoe_mock)
|
|
def test_table_cell(app, admin_user, statistics):
|
|
page = Page(title='One', slug='index')
|
|
page.save()
|
|
|
|
app = login(app)
|
|
|
|
cell = ChartNgCell(page=page, order=1, placeholder='content')
|
|
cell.statistic = Statistic.objects.get(slug='example')
|
|
cell.chart_type = 'table'
|
|
cell.save()
|
|
location = '/api/dataviz/graph/%s/' % cell.id
|
|
resp = app.get(location)
|
|
resp = app.get('/')
|
|
assert resp.text.count('Total') == 1
|
|
|
|
cell.statistic = Statistic.objects.get(slug='second')
|
|
cell.save()
|
|
resp = app.get(location)
|
|
resp = app.get('/')
|
|
assert resp.text.count('Total') == 1
|
|
|
|
cell.statistic = Statistic.objects.get(slug='third')
|
|
cell.save()
|
|
resp = app.get(location)
|
|
resp = app.get('/')
|
|
assert '114' in resp.text
|
|
assert resp.text.count('Total') == 2
|
|
|
|
cell.statistic = Statistic.objects.get(slug='fourth')
|
|
cell.save()
|
|
resp = app.get(location)
|
|
resp = app.get('/')
|
|
assert resp.text.count('Total') == 0
|
|
|
|
# total of durations is not computed
|
|
cell.statistic = Statistic.objects.get(slug='eighth')
|
|
cell.save()
|
|
resp = app.get(location)
|
|
resp = app.get('/')
|
|
assert resp.text.count('Total') == 0
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_table_cell_new_api(app, admin_user, new_api_statistics):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell(page=page, order=1, placeholder='content')
|
|
cell.statistic = Statistic.objects.get(slug='one-serie')
|
|
cell.chart_type = 'table'
|
|
cell.save()
|
|
|
|
app = login(app)
|
|
resp = app.get('/')
|
|
assert resp.text.count('Total') == 1
|
|
|
|
cell.statistic = Statistic.objects.get(slug='two-series')
|
|
cell.save()
|
|
resp = app.get('/')
|
|
assert '21' in resp.text
|
|
assert resp.text.count('Total') == 2
|
|
|
|
cell.statistic = Statistic.objects.get(slug='no-data')
|
|
cell.save()
|
|
resp = app.get('/')
|
|
assert resp.text.count('Total') == 0
|
|
|
|
|
|
def test_dataviz_hourly_unavailable_statistic(statistics, nocache):
|
|
all_stats_count = Statistic.objects.count()
|
|
assert Statistic.objects.filter(available=True).count() == all_stats_count
|
|
|
|
def bijoe_mock_unavailable(url, request):
|
|
visualization_json = VISUALIZATION_JSON[2:]
|
|
return {'content': json.dumps(visualization_json), 'request': request, 'status_code': 200}
|
|
|
|
appconfig = apps.get_app_config('dataviz')
|
|
with HTTMock(bijoe_mock_unavailable):
|
|
appconfig.hourly()
|
|
assert Statistic.objects.filter(available=True).count() == all_stats_count - 2
|
|
|
|
|
|
def test_dataviz_import_cell():
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell.objects.create(page=page, order=1, slug='test', placeholder='content')
|
|
statistic = Statistic.objects.create(
|
|
slug='example', site_slug='plop', service_slug='bijoe', url='https://example.org'
|
|
)
|
|
cell.statistic = statistic
|
|
cell.save()
|
|
|
|
site_export = [page.get_serialized_page()]
|
|
cell.delete()
|
|
|
|
Page.load_serialized_pages(site_export)
|
|
cell = ChartNgCell.objects.get(slug='test')
|
|
assert cell.statistic.pk == statistic.pk
|
|
|
|
cell.delete()
|
|
statistic.delete()
|
|
|
|
Page.load_serialized_pages(site_export)
|
|
cell = ChartNgCell.objects.get(slug='test')
|
|
assert cell.statistic.slug == statistic.slug
|
|
assert cell.statistic.site_slug == statistic.site_slug
|
|
assert cell.statistic.service_slug == statistic.service_slug
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_dataviz_api_list_statistics(new_api_statistics, settings):
|
|
statistic = Statistic.objects.get(slug='one-serie')
|
|
assert statistic.label == 'One serie stat'
|
|
assert statistic.site_slug == 'connection'
|
|
assert statistic.service_slug == 'authentic'
|
|
assert statistic.site_title == 'Connection'
|
|
assert statistic.url == 'https://authentic.example.com/api/statistics/one-serie/'
|
|
assert statistic.available
|
|
|
|
# try with external url
|
|
statistics_count = Statistic.objects.count()
|
|
settings.STATISTICS_PROVIDERS.append(
|
|
{'url': 'https://stat.com/stats/', 'id': 'example', 'name': 'Example Provider'}
|
|
)
|
|
catalog = {'data': [{'url': 'https://stat.com/stats/1/', 'name': 'Test', 'id': 'test'}]}
|
|
|
|
@urlmatch(scheme='https', netloc=r'stat.com', path='/stats/')
|
|
def server_error(url, request):
|
|
return {'content': 'error', 'status_code': 500}
|
|
|
|
appconfig = apps.get_app_config('dataviz')
|
|
with HTTMock(server_error):
|
|
appconfig.hourly()
|
|
assert Statistic.objects.count() == statistics_count
|
|
|
|
@urlmatch(scheme='https', netloc=r'stat.com', path='/stats/')
|
|
def success(url, request):
|
|
return {'content': json.dumps(catalog), 'status_code': 200}
|
|
|
|
with HTTMock(success):
|
|
appconfig.hourly()
|
|
|
|
assert Statistic.objects.count() == statistics_count + 1
|
|
statistic = Statistic.objects.get(slug='test')
|
|
assert statistic.label == 'Test'
|
|
assert statistic.site_slug == 'example'
|
|
assert statistic.service_slug == 'example'
|
|
assert statistic.site_title == 'Example Provider'
|
|
assert statistic.url == 'https://stat.com/stats/1/'
|
|
assert statistic.available
|
|
|
|
settings.STATISTICS_PROVIDERS.append('unknown')
|
|
appconfig = apps.get_app_config('dataviz')
|
|
with HTTMock(success):
|
|
appconfig.hourly() # unknown provider is ignored
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
@pytest.mark.parametrize('date', ['2020-03-02 12:01', '2020-03-05 12:01']) # Monday and Thursday
|
|
def test_chartng_cell_new_api_filter_params(new_api_statistics, nocache, freezer, date):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell(page=page, order=1, placeholder='content')
|
|
cell.statistic = Statistic.objects.get(slug='one-serie')
|
|
cell.save()
|
|
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][0]
|
|
assert 'time_interval=' not in request.url
|
|
assert 'ou=' not in request.url
|
|
|
|
cell.filter_params = {'time_interval': 'month', 'ou': 'default'}
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][1]
|
|
assert 'time_interval=month' in request.url
|
|
assert 'ou=default' in request.url
|
|
|
|
freezer.move_to(date)
|
|
cell.time_range = 'previous-year'
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][2]
|
|
assert 'time_interval=month' in request.url
|
|
assert 'ou=default' in request.url
|
|
assert 'start=2019-01-01' in request.url and 'end=2020-01-01' in request.url
|
|
|
|
cell.time_range = 'current-week'
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][-1]
|
|
assert 'start=2020-03-02' in request.url and 'end=2020-03-09' in request.url
|
|
|
|
cell.time_range = 'previous-week'
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][-1]
|
|
assert 'start=2020-02-24' in request.url and 'end=2020-03-02' in request.url
|
|
|
|
cell.time_range = 'next-week'
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][-1]
|
|
assert 'start=2020-03-09' in request.url and 'end=2020-03-16' in request.url
|
|
|
|
cell.time_range = 'range'
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][-1]
|
|
assert 'start' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
|
|
assert 'end' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
|
|
|
|
cell.time_range_start = '2020-10-01'
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][-1]
|
|
assert 'start=2020-10-01' in request.url
|
|
|
|
cell.time_range_end = '2020-11-03'
|
|
cell.save()
|
|
chart = cell.get_chart()
|
|
request = new_api_mock.call['requests'][-1]
|
|
assert 'start=2020-10-01' in request.url and 'end=2020-11-03' in request.url
|
|
|
|
|
|
def test_dataviz_check_validity(nocache):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
stat = Statistic.objects.create(url='https://stat.com/stats/1/')
|
|
cell = ChartNgCell.objects.create(page=page, order=1, placeholder='content', statistic=stat)
|
|
|
|
@urlmatch(scheme='https', netloc=r'stat.com', path='/stats/1/')
|
|
def url_mock(url, request):
|
|
return {'content': json.dumps({'data': [], 'err': 0}), 'status_code': 200}
|
|
|
|
with HTTMock(url_mock):
|
|
cell.check_validity()
|
|
assert ValidityInfo.objects.exists() is False
|
|
|
|
@urlmatch(scheme='https', netloc=r'stat.com', path='/stats/1/')
|
|
def url_mock(url, request):
|
|
return {'content': json.dumps({'data': [], 'err': 1}), 'status_code': 404}
|
|
|
|
with HTTMock(url_mock):
|
|
cell.check_validity()
|
|
validity_info = ValidityInfo.objects.latest('pk')
|
|
assert validity_info.invalid_reason_code == 'statistic_data_not_found'
|
|
|
|
stat.url = ''
|
|
stat.save()
|
|
cell.check_validity()
|
|
validity_info = ValidityInfo.objects.latest('pk')
|
|
assert validity_info.invalid_reason_code == 'missing_statistic_url'
|
|
|
|
|
|
@with_httmock(new_api_mock)
|
|
def test_chartng_cell_new_api_aggregation(new_api_statistics, app, admin_user, nocache):
|
|
page = Page.objects.create(title='One', slug='index')
|
|
cell = ChartNgCell(page=page, order=1, placeholder='content')
|
|
cell.statistic = Statistic.objects.get(slug='daily')
|
|
cell.save()
|
|
|
|
app = login(app)
|
|
resp = app.get('/manage/pages/%s/' % page.id)
|
|
time_interval_field = resp.form['cdataviz_chartngcell-%s-time_interval' % cell.id]
|
|
assert time_interval_field.value == 'day'
|
|
assert time_interval_field.options == [
|
|
('day', True, 'Day'),
|
|
('_week', False, 'Week'),
|
|
('_month', False, 'Month'),
|
|
('_year', False, 'Year'),
|
|
('_weekday', False, 'Week day'),
|
|
]
|
|
resp.form.submit()
|
|
cell.refresh_from_db()
|
|
|
|
chart = cell.get_chart()
|
|
assert len(chart.x_labels) == 484
|
|
assert chart.x_labels[:3] == ['06-10-2020', '07-10-2020', '08-10-2020']
|
|
assert chart.x_labels[-3:] == ['30-01-2022', '31-01-2022', '01-02-2022']
|
|
assert chart.raw_series[0][0][:8] == [0, 0, 0, 0, 0, 0, 0, 1]
|
|
assert chart.raw_series[1][0][:8] == [2, 0, 0, 0, 0, 0, 0, 2]
|
|
|
|
time_interval_field.value = '_month'
|
|
resp.form.submit()
|
|
cell.refresh_from_db()
|
|
|
|
chart = cell.get_chart()
|
|
assert 'time_interval=day' in new_api_mock.call['requests'][1].url
|
|
assert len(chart.x_labels) == 17
|
|
assert chart.x_labels[:3] == ['10-2020', '11-2020', '12-2020']
|
|
assert chart.x_labels[-3:] == ['12-2021', '01-2022', '02-2022']
|
|
assert chart.raw_series == [
|
|
([1, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2], {'title': 'Serie 1'}),
|
|
([4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], {'title': 'Serie 2'}),
|
|
]
|
|
|
|
time_interval_field.value = '_year'
|
|
resp.form.submit()
|
|
cell.refresh_from_db()
|
|
|
|
chart = cell.get_chart()
|
|
assert 'time_interval=day' in new_api_mock.call['requests'][2].url
|
|
assert chart.x_labels == ['2020', '2021', '2022']
|
|
assert chart.raw_series == [
|
|
([17, 0, 2], {'title': 'Serie 1'}),
|
|
([5, 0, 0], {'title': 'Serie 2'}),
|
|
]
|
|
|
|
time_interval_field.value = '_weekday'
|
|
resp.form.submit()
|
|
cell.refresh_from_db()
|
|
|
|
chart = cell.get_chart()
|
|
assert 'time_interval=day' in new_api_mock.call['requests'][3].url
|
|
assert chart.x_labels == ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
|
|
assert chart.raw_series == [
|
|
([16, 3, 0, 0, 0, 0, 0], {'title': 'Serie 1'}),
|
|
([1, 4, 0, 0, 0, 0, 0], {'title': 'Serie 2'}),
|
|
]
|
|
|
|
time_interval_field.value = '_week'
|
|
resp.form.submit()
|
|
cell.refresh_from_db()
|
|
|
|
chart = cell.get_chart()
|
|
assert 'time_interval=day' in new_api_mock.call['requests'][1].url
|
|
assert len(chart.x_labels) == 70
|
|
assert chart.x_labels[:3] == ['W41-2020', 'W42-2020', 'W43-2020']
|
|
assert chart.x_labels[-6:] == ['W52-2021', 'W1-2022', 'W2-2022', 'W3-2022', 'W4-2022', 'W5-2022']
|
|
assert chart.raw_series == [
|
|
([0, 1, 0, 0, 0, 0, 0, 0, 16] + [0] * 60 + [2], {'title': 'Serie 1'}),
|
|
([2, 2, 0, 0, 0, 0, 0, 0, 1] + [0] * 61, {'title': 'Serie 2'}),
|
|
]
|
|
|
|
cell.statistic = Statistic.objects.get(slug='leap-week')
|
|
cell.save()
|
|
resp = app.get('/manage/pages/%s/' % page.id)
|
|
resp.form.submit()
|
|
chart = cell.get_chart()
|
|
assert 'time_interval=day' in new_api_mock.call['requests'][1].url
|
|
assert len(chart.x_labels) == 1
|
|
assert chart.x_labels == ['W53-2020']
|
|
assert chart.raw_series == [([19], {'title': 'Serie 1'})]
|