From 27186abb01aa272056d780740768c1820cbb100a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sat, 17 Mar 2018 23:18:41 +0100 Subject: [PATCH] dataviz: remove legacy cubes code (#12743) --- combo/apps/dataviz/__init__.py | 9 - combo/apps/dataviz/forms.py | 82 +-------- combo/apps/dataviz/models.py | 162 +----------------- .../templates/combo/cubes-barchart.html | 15 -- .../dataviz/templates/combo/cubes-table.html | 23 --- combo/apps/dataviz/utils.py | 152 ---------------- combo/settings.py | 1 - debian/control | 1 - debian/pydist-overrides | 1 - requirements.txt | 1 - setup.py | 1 - 11 files changed, 2 insertions(+), 446 deletions(-) delete mode 100644 combo/apps/dataviz/templates/combo/cubes-barchart.html delete mode 100644 combo/apps/dataviz/templates/combo/cubes-table.html delete mode 100644 combo/apps/dataviz/utils.py diff --git a/combo/apps/dataviz/__init__.py b/combo/apps/dataviz/__init__.py index 0740f934..8fe74c4c 100644 --- a/combo/apps/dataviz/__init__.py +++ b/combo/apps/dataviz/__init__.py @@ -30,13 +30,4 @@ class AppConfig(django.apps.AppConfig): from . import urls return urls.urlpatterns - def ready(self): - @checks.register('settings') - def check_settings(**kwargs): - # Check if CUBES_URL is a proper URL string - if (getattr(settings, 'CUBES_URL', None) is not None - and (not isinstance(settings.CUBES_URL, str) - or not re.match(r'https?://', settings.CUBES_URL))): - yield checks.Error('settings.CUBES_URL must be an HTTP URL') - default_app_config = 'combo.apps.dataviz.AppConfig' diff --git a/combo/apps/dataviz/forms.py b/combo/apps/dataviz/forms.py index 0063afce..a137929f 100644 --- a/combo/apps/dataviz/forms.py +++ b/combo/apps/dataviz/forms.py @@ -14,17 +14,12 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import json - -from django.utils.translation import ugettext_lazy as _ from django import forms from django.conf import settings -from django.core.exceptions import ValidationError from combo.utils import requests -from .models import BaseCubesChart, ChartCell -from .utils import get_cubes, get_cube, get_drilldown +from .models import ChartCell class ChartForm(forms.ModelForm): @@ -42,78 +37,3 @@ class ChartForm(forms.ModelForm): available_charts.extend([(x['path'], x['name']) for x in result]) available_charts.sort(key=lambda x: x[1]) self.fields['url'].widget = forms.Select(choices=available_charts) - - -class CubesBarChartForm(forms.ModelForm): - EMPTY = [(u'', _('None'))] - - class Meta: - model = BaseCubesChart - fields = ('title', 'url', 'cube', 'aggregate1', 'drilldown1', 'drilldown2', - 'other_parameters') - - def __init__(self, *args, **kwargs): - super(CubesBarChartForm, self).__init__(*args, **kwargs) - for field in ('cube', 'aggregate1', 'drilldown1', 'drilldown2'): - self.fields[field] = forms.ChoiceField( - label=self.fields[field].label, - initial=self.fields[field].initial, - required=False, - choices=self.EMPTY) - if getattr(settings, 'CUBES_URL', None): - cube_choices = self.get_cubes_choices() - if cube_choices: - self.fields['cube'].choices = cube_choices - aggregate1_choices = self.get_aggregate_choices() - # If there is no choice, hide the selector - if not aggregate1_choices or len(aggregate1_choices) < 3: - self.fields['aggregate1'].widget = forms.HiddenInput() - if aggregate1_choices: - self.fields['aggregate1'].choices = aggregate1_choices - drilldown_choices = self.get_drilldown_choices() - if drilldown_choices: - self.fields['drilldown1'].choices = drilldown_choices - self.fields['drilldown2'].choices = drilldown_choices - - def clean(self): - cleaned_data = self.cleaned_data - if getattr(settings, 'CUBES_URL', None): - aggregate1_choices = self.get_aggregate_choices() - # If there is no choice, autoselect - if aggregate1_choices and len(aggregate1_choices) == 2: - cleaned_data['aggregate1'] = aggregate1_choices[1][0] - return cleaned_data - - def clean_other_parameters(self): - other_parameters = self.cleaned_data['other_parameters'] - if other_parameters: - try: - decoded = json.loads(other_parameters) - assert isinstance(decoded, dict) - for key, value in decoded.iteritems(): - assert isinstance(key, unicode) - assert isinstance(value, unicode) - except (ValueError, AssertionError): - raise ValidationError(_('Other parameter must be a JSON object containing only ' - 'strings')) - return other_parameters - - def get_cubes_choices(self): - cubes = get_cubes() - return self.EMPTY + [(cube['name'], cube.get('label')) for cube in cubes] - - def get_aggregate_choices(self): - cube = self.data.get(self.add_prefix('cube')) if self.data else self.instance.cube - if cube: - cube = get_cube(cube) - if cube: - return self.EMPTY + [(ag['name'], ag['label']) for ag in cube.get('aggregates', [])] - return [] - - def get_drilldown_choices(self): - cube = self.data.get(self.add_prefix('cube')) if self.data else self.instance.cube - if cube: - choices = get_drilldown(cube) - if choices: - return self.EMPTY + choices - return [] diff --git a/combo/apps/dataviz/models.py b/combo/apps/dataviz/models.py index 779fdcef..828708b0 100644 --- a/combo/apps/dataviz/models.py +++ b/combo/apps/dataviz/models.py @@ -14,9 +14,6 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -import json -from collections import OrderedDict - from django.core.urlresolvers import reverse from django.db import models from django.utils.translation import ugettext_lazy as _ @@ -24,9 +21,8 @@ from django.conf import settings from combo.data.models import CellBase from combo.data.library import register_cell_class -from combo.utils import NothingInCacheException, get_templated_url +from combo.utils import get_templated_url -from . import utils @register_cell_class class Gauge(CellBase): @@ -92,159 +88,3 @@ class ChartCell(CellBase): context['title'] = self.title context['url'] = self.url return context - -class BaseCubesChart(CellBase): - title = models.CharField(_('Title'), max_length=150, blank=True, null=True) - url = models.URLField(_('URL'), max_length=150, blank=True, null=True) - cube = models.CharField(verbose_name=_('Form'), max_length=256, blank=True, null=True) - aggregate1 = models.CharField(verbose_name=_('Aggregate'), max_length=64, blank=True, null=True) - drilldown1 = models.CharField(verbose_name=_('Criterion 1'), max_length=64, blank=True, - null=True) - drilldown2 = models.CharField(verbose_name=_('Criterion 2'), max_length=64, blank=True, - null=True) - other_parameters = models.TextField(verbose_name=_('Other parameters'), blank=True, null=True) - - class Meta: - abstract = True - - @classmethod - def is_enabled(self): - return bool(getattr(settings, 'CUBES_URL', None)) - - def get_additional_label(self): - return self.title - - def get_default_form_class(self): - from .forms import CubesBarChartForm - return CubesBarChartForm - - def get_cell_extra_context(self, context): - ctx = { - 'cell': self, - 'title': self.title, - 'url': self.url, - 'aggregate': self.get_aggregate(context=context), - } - cube = utils.get_cube(self.cube) - aggregates = dict((ag['name'], ag['label']) for ag in cube.get('aggregates', [])) - drilldowns = utils.get_drilldown(self.cube) - if self.aggregate1 and aggregates: - ctx['aggregate1_label'] = aggregates.get(self.aggregate1) - if self.drilldown1 and drilldowns: - ctx['drilldown1_label'] = dict(drilldowns).get(self.drilldown1) - if self.drilldown2 and drilldowns: - ctx['drilldown2_label'] = dict(drilldowns).get(self.drilldown2) - return ctx - - def get_aggregate(self, context={}): - '''Get aggregate defined by chosen cube and the two drildown paths, request ordering of the - data by natural order of each axis.''' - from .utils import get_aggregate, get_cube, compute_levels - def simplify_integers(l): - for x in l: - if isinstance(x, float): - if x - round(x) < 0.001: - x = int(x) - yield x - - other_parameters = json.loads(self.other_parameters) if self.other_parameters else {} - if context and 'parameters' in context: - parameters = context['parameters'] - for key in parameters: - if not key.startswith('cubes-cut-'): - continue - name = key.split(u'cubes-cut-', 1)[1] - value = parameters[key] - new_cut = u'%s:%s' % (name, value) - if other_parameters.get('cut'): - other_parameters['cut'] = other_parameters['cut'] + u'|' + new_cut - else: - other_parameters['cut'] = new_cut - - aggregate = get_aggregate(name=self.cube, - aggregate1=self.aggregate1, - drilldown1=self.drilldown1, - drilldown2=self.drilldown2, - other_parameters=other_parameters) - - cube = get_cube(self.cube) - if not aggregate or not cube: - return - - label_refs1 = [] - key_refs1 = [] - if self.drilldown1: - compute_levels(cube, self.drilldown1, label_refs=label_refs1, key_refs=key_refs1) - key_refs2 = [] - label_refs2 = [] - if self.drilldown2: - compute_levels(cube, self.drilldown2, label_refs=label_refs2, key_refs=key_refs2) - for ag in cube['aggregates']: - if ag['name'] != self.aggregate1: - continue - break - - def cell_ref(cell, refs): - return tuple(cell[ref] for ref in refs) - - keys1 = OrderedDict() - labels = OrderedDict() - datasets = OrderedDict() - - for cell in aggregate['cells']: - label1 = u' / '.join(map(unicode, simplify_integers(cell_ref(cell, label_refs1)))) - key1 = cell_ref(cell, key_refs1) - labels[key1] = label1 - keys1[key1] = 1 - if key_refs2: - label2 = u' / '.join(map(unicode, simplify_integers(cell_ref(cell, label_refs2)))) - key2 = cell_ref(cell, key_refs2) - else: - label2 = '' - key2 = 1 - dataset = datasets.setdefault(key2, {'label': label2, - 'data': OrderedDict()}) - value = cell[self.aggregate1] - dataset['data'][key1] = value - for dataset in datasets.itervalues(): - dataset['data'] = [dataset['data'].get(key, 0) for key in keys1] - - return { - 'labels': labels.values(), - 'datasets': [{ - 'label': dataset['label'], - 'data': dataset['data'], - } for dataset in datasets.itervalues()] - } - - def render(self, context): - if not context.get('synchronous'): - raise NothingInCacheException() - return super(BaseCubesChart, self).render(context) - - def render_for_search(self): - return '' - - -@register_cell_class -class CubesBarChart(BaseCubesChart): - template_name = 'combo/cubes-barchart.html' - - class Media: - js = ('xstatic/ChartNew.js', 'js/combo.cubes-barchart.js') - - class Meta: - verbose_name = _('Cubes Barchart') - - def get_cell_extra_context(self, context): - ctx = super(CubesBarChart, self).get_cell_extra_context(context) - # Need JSON serialization to pass data to Chart.js - ctx['json_aggregate'] = json.dumps(ctx['aggregate']) - return ctx - -@register_cell_class -class CubesTable(BaseCubesChart): - template_name = 'combo/cubes-table.html' - - class Meta: - verbose_name = _('Cubes Table') diff --git a/combo/apps/dataviz/templates/combo/cubes-barchart.html b/combo/apps/dataviz/templates/combo/cubes-barchart.html deleted file mode 100644 index 1e20ef1e..00000000 --- a/combo/apps/dataviz/templates/combo/cubes-barchart.html +++ /dev/null @@ -1,15 +0,0 @@ -{% if title %} -

{% if url %}{% endif %}{{title}}{% if url %}{% endif %}

-{% endif %} - -
- - -
diff --git a/combo/apps/dataviz/templates/combo/cubes-table.html b/combo/apps/dataviz/templates/combo/cubes-table.html deleted file mode 100644 index 59784da7..00000000 --- a/combo/apps/dataviz/templates/combo/cubes-table.html +++ /dev/null @@ -1,23 +0,0 @@ -{% if title %} -

- {% if url %}{% endif %}{{title}}{% if url %}{% endif %} -

-{% endif %} - - - - {% for label in aggregate.labels %} - - {% endfor %} - - - {% for dataset in aggregate.datasets %} - - - {% for value in dataset.data %} - - {% endfor %} - - {% endfor %} - -
{{ label }}
{{ dataset.label }}{{ value }}
diff --git a/combo/apps/dataviz/utils.py b/combo/apps/dataviz/utils.py deleted file mode 100644 index cb4fd874..00000000 --- a/combo/apps/dataviz/utils.py +++ /dev/null @@ -1,152 +0,0 @@ -# combo - content management system -# Copyright (C) 2015 Entr'ouvert -# -# This program is free software: you can redistribute it and/or modify it -# under the terms of the GNU Affero General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . - -import requests -from requests.exceptions import RequestException -import urlparse - -from django.utils.translation import ugettext as _ -from django.conf import settings - - -def get_requests_params(): - return getattr(settings, 'CUBES_REQUESTS_PARAMS', {}) - - -def get_cubes(): - try: - r = requests.get(urlparse.urljoin(settings.CUBES_URL, 'cubes'), **get_requests_params()) - except RequestException: - return [] - try: - return r.json() - except ValueError: - return [] - - -def get_cube(name): - model_url = urlparse.urljoin(settings.CUBES_URL, 'cube/%s/model' % name) - try: - r = requests.get(model_url, **get_requests_params()) - except RequestException: - return None - try: - return r.json() - except ValueError: - return None - - -def get_drilldown(name): - cube = get_cube(name) - if not cube: - return [] - l = [] - seen = set() - for dimension in cube.get('dimensions', []): - dim_name = dimension['name'] - dim_label = dimension.get('label') or dim_name - if dimension.get('levels'): - levels = {} - for level in dimension['levels']: - levels[level['name']] = level.get('label') or level['name'] - if dimension.get('hierarchies'): - for hierarchy in dimension['hierarchies']: - h_name = hierarchy['name'] - h_label = hierarchy.get('label') or h_name - if h_name == 'default': - h_label = '' - for i in range(1, len(hierarchy['levels'])+1): - level = hierarchy['levels'][i-1] - label = _(u'by ') + _(u' and ').join( - levels[level] for level in hierarchy['levels'][:i] - ) - name = '%s@%s:%s' % (dim_name, h_name, level) - if label not in seen: - l.append((name, label)) - seen.add(label) - else: - raise NotImplementedError - else: - l.append((dim_name, _(u'by %s') % dim_label)) - return l - - -def compute_levels(cube, drilldown, key_refs=None, label_refs=None): - from .utils import get_attribute_ref - dim = drilldown.split('@')[0] - hier = drilldown.split('@')[1].split(':')[0] - lev = drilldown.split(':')[1] - - for dimension in cube['dimensions']: - if dimension['name'] != dim: - continue - level_label_refs = {} - level_key_refs = {} - for level in dimension['levels']: - level_key_refs[level['name']] = get_attribute_ref(level, level['key']) - level_label_refs[level['name']] = get_attribute_ref(level, level['label_attribute']) - for hierarchy in dimension['hierarchies']: - if hierarchy['name'] != hier: - continue - for level in hierarchy['levels']: - if key_refs is not None: - key_refs.append(level_key_refs[level]) - if label_refs is not None: - label_refs.append(level_label_refs[level]) - if level == lev: - break - break - break - - -def get_aggregate(name, aggregate1, drilldown1, drilldown2, other_parameters=None): - if not name: - return None - cube = get_cube(name) - aggregate_url = urlparse.urljoin(settings.CUBES_URL, 'cube/%s/aggregate' % name) - if not aggregate1: - return None - try: - params = {'aggregate': aggregate1} - drilldowns = [] - key_refs = [] - if drilldown1: - compute_levels(cube, drilldown1, key_refs=key_refs) - drilldowns.append(drilldown1) - if drilldown2: - compute_levels(cube, drilldown2, key_refs=key_refs) - drilldowns.append(drilldown2) - if drilldowns: - params['drilldown'] = drilldowns - - if key_refs: - params['order'] = key_refs - if other_parameters: - params.update(other_parameters) - - r = requests.get(aggregate_url, params=params, **get_requests_params()) - except RequestException: - return None - try: - return r.json() - except ValueError: - return None - - -def get_attribute_ref(level, name): - for attribute in level['attributes']: - if attribute['name'] == name: - return attribute['ref'] diff --git a/combo/settings.py b/combo/settings.py index 22d064c4..e3a3bac6 100644 --- a/combo/settings.py +++ b/combo/settings.py @@ -77,7 +77,6 @@ INSTALLED_APPS = ( 'combo.apps.maps', 'combo.apps.calendar', 'haystack', - 'xstatic.pkg.chartnew_js', 'xstatic.pkg.josefinsans', 'xstatic.pkg.leaflet', 'xstatic.pkg.opensans', diff --git a/debian/control b/debian/control index 32bbc9f5..0f8e7aa9 100644 --- a/debian/control +++ b/debian/control @@ -14,7 +14,6 @@ Depends: ${misc:Depends}, ${python:Depends}, python-gadjo, python-requests, python-feedparser, - python-xstatic-chartnew-js, python-xstatic-josefinsans, python-xstatic-leaflet, python-xstatic-leaflet-markercluster, diff --git a/debian/pydist-overrides b/debian/pydist-overrides index 703e56fa..0c6fbc3e 100644 --- a/debian/pydist-overrides +++ b/debian/pydist-overrides @@ -1,2 +1 @@ django_ckeditor python-django-ckeditor -XStatic-ChartNew.js python-xstatic-chartnew-js diff --git a/requirements.txt b/requirements.txt index ea18253e..e5c11803 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,6 @@ gadjo feedparser django-jsonfield requests -XStatic-ChartNew.js XStatic-Leaflet XStatic_JosefinSans XStatic_OpenSans diff --git a/setup.py b/setup.py index fede8d8c..20049306 100644 --- a/setup.py +++ b/setup.py @@ -140,7 +140,6 @@ setup( 'feedparser', 'django-jsonfield', 'requests', - 'XStatic-ChartNew.js', 'XStatic-Leaflet', 'XStatic-Leaflet-MarkerCluster', 'XStatic_JosefinSans',